﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace Kiiro.Model.Utility
{
    public static class ExtensionMethods
    {
        #region AsSafeEnumerable - SPWebCollection

        public static IEnumerable<SPWeb> AsSafeEnumerable ( this SPWebCollection webs )
        {
            foreach ( SPWeb web in webs )
            {
                try
                {
                    yield return web;
                }
                finally
                {
                    web.Dispose ( );
                }
            }
        }

        public static IEnumerable<SPWeb> Where ( this SPWebCollection webs, Func<SPWeb, bool> predicate )
        {
            return webs.AsSafeEnumerable ( ).Where ( predicate );
        }

        public static void ForEach ( this SPWebCollection webs, Action<SPWeb> action, Func<SPWeb, bool> predicate )
        {
            foreach ( SPWeb web in webs.Where ( predicate ) )
                action ( web );
        }

        public static void ForEach ( this SPWebCollection webs, Action<SPWeb> action )
        {
            webs.ForEach ( action, w => true );
        }

        public static IEnumerable<TResult> Select<TResult> ( this SPWebCollection webs, Func<SPWeb, TResult> selector, Func<SPWeb, bool> predicate )
        {
            return webs.Where ( predicate ).Select ( selector );
        }

        public static IEnumerable<TResult> Select<TResult> ( this SPWebCollection webs, Func<SPWeb, TResult> selector )
        {
            return webs.AsSafeEnumerable ( ).Select ( selector );
        }

        #endregion

        #region AsSafeEnumerable - SPSiteCollection

        public static IEnumerable<SPSite> AsSafeEnumerable ( this SPSiteCollection sites )
        {
            foreach ( SPSite site in sites )
            {
                try
                {
                    yield return site;
                }
                finally
                {
                    site.Dispose ( );
                }
            }
        }

        public static IEnumerable<SPSite> Where ( this SPSiteCollection sites, Func<SPSite, bool> predicate )
        {
            return sites.AsSafeEnumerable ( ).Where ( predicate );
        }

        public static void ForEach ( this SPSiteCollection sites, Action<SPSite> action, Func<SPSite, bool> predicate )
        {
            foreach ( SPSite site in sites.Where ( predicate ) )
                action ( site );
        }

        public static void ForEach ( this SPSiteCollection sites, Action<SPSite> action )
        {
            sites.ForEach ( action, w => true );
        }

        public static IEnumerable<TResult> Select<TResult> ( this SPSiteCollection sites, Func<SPSite, TResult> selector, Func<SPSite, bool> predicate )
        {
            return sites.Where ( predicate ).Select ( selector );
        }

        public static IEnumerable<TResult> Select<TResult> ( this SPSiteCollection sites, Func<SPSite, TResult> selector )
        {
            return sites.AsSafeEnumerable ( ).Select ( selector );
        }

        #endregion

        #region Functionals - Select Using
        public static TResult Select<T, TResult> ( Func<T> opener, Func<T, TResult> selector, Action<T> closer )
        {
            T openerResult = opener ( );
            try
            {
                return selector ( openerResult );
            }
            finally
            {

                closer ( openerResult );
            }
        }

        public static TResult SelectUsing<T, TResult> ( Func<T> opener, Func<T, TResult> selector, Action<T> closer ) where T : IDisposable
        {
            return Select ( opener, selector, closer );
        }

        public static TResult SelectFromWeb<TResult> ( this SPSite site, string url, Func<SPWeb, TResult> selector )
        {
            return SelectUsing ( ( ) => site.OpenWeb ( url ), selector, x => x.Dispose ( ) );
        }
        #endregion

        #region Security - RunAsSystem
        public static SPUserToken GetSystemToken ( this SPSite site )
        {
            SPUserToken token = null;
            bool tempCADE = site.CatchAccessDeniedException;
            try
            {
                site.CatchAccessDeniedException = false;
                token = site.SystemAccount.UserToken;
            }
            catch ( UnauthorizedAccessException )
            {
                SPSecurity.RunWithElevatedPrivileges ( ( ) =>
                {
                    using ( SPSite elevSite = new SPSite ( site.ID ) )
                        token = elevSite.SystemAccount.UserToken;
                } );
            }
            finally
            {
                site.CatchAccessDeniedException = tempCADE;
            }
            return token;
        }

        public static void RunAsSystem ( this SPSite site, Action<SPSite> action )
        {
            using ( SPSite elevSite = new SPSite ( site.ID, site.GetSystemToken ( ) ) )
                action ( elevSite );
        }
        public static void RunAsSystem ( this SPSite site, Guid webId, Action<SPWeb> action )
        {
            site.RunAsSystem ( s => action ( s.OpenWeb ( webId ) ) );
        }

        public static void RunAsSystem ( this SPSite site, string url, Action<SPWeb> action )
        {
            site.RunAsSystem ( s => action ( s.OpenWeb ( url ) ) );
        }

        public static void RunAsSystem ( this SPWeb web, Action<SPWeb> action )
        {
            web.Site.RunAsSystem ( web.ID, action );
        }
        #endregion

        #region Security - SelectAsSystem
        public static T SelectAsSystem<T> ( this SPSite site, Func<SPSite, T> selector )
        {
            using ( SPSite elevSite = new SPSite ( site.ID, site.GetSystemToken ( ) ) )
                return selector ( elevSite );
        }

        public static T SelectAsSystem<T> ( this SPSite site, Guid webId, Func<SPWeb, T> selector )
        {
            return site.SelectAsSystem ( s => selector ( s.OpenWeb ( webId ) ) );
        }

        public static T SelectAsSystem<T> ( this SPSite site, string url, Func<SPWeb, T> selector )
        {
            return site.SelectAsSystem ( s => selector ( s.OpenWeb ( url ) ) );
        }

        public static T SelectAsSystem<T> ( this SPWeb web, Func<SPWeb, T> selector )
        {
            return web.Site.SelectAsSystem ( web.ID, selector );
        }
        #endregion

        #region Distinct Delegate Comparer
        public static IEnumerable<T> Distinct<T> ( this IEnumerable<T> source, Func<T, T, bool> comparer )
        {
            return source.Distinct ( new DelegateComparer<T> ( comparer ) );
        }

        public static IEnumerable<T> Distinct<T> ( this IEnumerable<T> source, Func<T, T, bool> comparer, Func<T, int> hashMethod )
        {
            return source.Distinct ( new DelegateComparer<T> ( comparer, hashMethod ) );
        }

        /// <summary>
        /// Custom extension method to allow for only comparing a User's Login instead of the whole User object
        /// </summary>
        public static IEnumerable<T> DistinctLogin<T> ( this IEnumerable<T> source, Func<T, T, bool> comparer )
        {
            return source.Distinct ( new DelegateUserLoginComparer<T> ( comparer ) );
        }
        #endregion
    }
}

/*************************************************************************
 * 
 * $Log: /Internal Projects/Kiiro/Kiiro.Model/Utility/ExtensionMethods.cs $
 * 
 * 9     15/12/09 11:49a Adamw
 * Case 418 added overloaded delegateComparer
 * 
 * 8     28/10/09 11:01a Camerons
 * 
 * 7     15/10/09 9:34a Camerons
 * 
 * 6     24/09/09 2:23p Camerons
 * 
 * 5     23/09/09 4:43p Camerons
 * Added extension methods for Using and ElevatedSites
 * 
 * 4     23/09/09 4:17p Camerons
 * 
 * 3     23/09/09 4:17p Camerons
 * 
 * 2     23/09/09 4:17p Camerons
 * Added comments section
 * 
 ************************************************************************/
